use crate::service;
use crate::service::{add_server, login_ssh, run_cli, scp_file, select_server, show_server, AppData, ParamType, PluginScript, WorkUtil};
use crate::utils::{init_config, AES_IV, AES_KEY};
use crate::{error, AES_IV_ARR, AES_KEY_ARR, WORK_DIR};
use anyhow::Result;
use clap::{Parser, Subcommand};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use strum::IntoEnumIterator;

const ABOUT: &str = r#"
    工具配置文件目录：
    win: C:\Users\Alice
    mac: /Users/Alice
    linux: /home/alice
    ------------------------------------------------------------------------------------------------
    init [-c FILE] 初始化配置文件 -c 指定配置文件路径 配置文件名称 deploy.toml
    server 服务器管理 - 新增服务器
        list 查询所有服务器信息
        -u[--update] [label] 修改服务器
        -d[--delete] [label] 删除服务器
        -s[--show] [label]   显示服务器信息
    ssh [server] 登录服务器 目前只支持 Unix
    scp [file] 上传：scp file ... [server]:[path]，下载：scp [server]:[path] ... file
    eidt [action] [file] [dir] 修改文件内容 action: '/old/new/' '1 /old/new/' '1,3 /old/new/' '/old\/str/new/' old or new 中有/时需要使用转义字符, file 可以是具体的文件路径也可以是文件名、文件名时需要带上 dir 参数指定搜索目录, dir 文件搜索目录如果指定了文件具体路径可省略
    代码远程部署配置文件说明：
        # command 可以使用变量{deployPath}, {targetName}, {targetPath}替换对应数据, targetPath只能在local.command中使用
        # 部署文件名称
        targetName = "test"
        # 部署文件目录，默认当前文件夹
        targetPath = ""
        # 配置不同环境设置 server name对应服务器管理中的label deploy_path 为远程部署路径
        [[profile.dev.server]]
        name = "test"
        deployPath= ""
        # 环境变量
        [profile.dev.env]
        test = 1
        [local]
        # 本地执行命令
        command = [""]
        [remote]
        # 远程执行命令
        command = ["cd {deployPath} && python3 {targetName}"]
        # 是否对远程文件进行备份
        backup = false
        # 备份文件路径
        backupFilePath = ""
        # 设置可执行文件的环境变量
        [execution]
        JAVA_HOME = ""
        # 部署完成后执行的命令，用于清理编译文件等。
        [after]
        command = ["echo 'success'"]
    "#;
#[derive(Parser)]
#[command(name="ToolBox", author="Rookie.<gb880327@189.cn>",version="1.1.0", about, long_about = Some(ABOUT))]
struct Cli {
    /// 配置文件路径！
    #[arg(short, long, value_name = "FILE")]
    config: Option<PathBuf>,

    #[command(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommand)]
enum Commands {
    /// 初始化配置文件！
    Init {
        /// 配置文件路径！
        #[arg(short, long, value_name = "FILE")]
        config: Option<PathBuf>
    },
    /// 服务器管理！
    Server {
        #[command(subcommand)]
        command: ServerCommand
    },
    /// 登录服务器！
    Ssh {
        /// 服务器名称！
        #[arg(index = 1, value_name = "server_name")]
        server: Option<String>
    },
    /// 文件上传下载！
    Scp {
        /// 上传： [file...] [server]:[path]，下载：[server]:[path...] file
        #[arg(action=clap::ArgAction::Append, value_name = "file")]
        file: Vec<String>
    },
    /// 修改文件内容！
    ///
    /// eidt [action] [file] [dir] 修改文件内容 action: '/old/new/' '1 /old/new/' '1,3 /old/new/' '/old\/str/new/' old or new 中有/时需要使用转义字符,
    /// file 可以是具体的文件路径也可以是文件名、文件名时需要带上 dir 参数指定搜索目录, dir 文件搜索目录如果指定了文件具体路径可省略
    Edit {
        /// 执行参数！
        #[arg(index = 1, value_name = "action")]
        action: String,
        /// 文件名称！
        #[arg(index = 2, value_name = "file")]
        file: String,
        /// 搜索目录！
        #[arg(index = 3, required = false, value_name = "dir")]
        dir: String,
    },
    /// 插件管理！
    Plugin {
        #[command(subcommand)]
        command: PluginCommands
    },
    /// 运行插件！
    Run {
        /// 插件名称！
        #[arg(index = 1, required = true, value_name = "plugin_name")]
        plugin: String,
        /// 插件参数！
        #[arg(index = 2, required = false, action=clap::ArgAction::Append, value_name = "args")]
        args: Option<Vec<String>>,
    },
}

#[derive(Subcommand)]
enum ServerCommand {
    /// 服务器列表！
    List {},
    /// 显示服务器信息！
    Show {
        /// 服务器名称！
        #[arg(index = 1, value_name = "label")]
        label: Option<String>
    },
    Add {},
    /// 删除服务器！
    Remove {
        /// 服务器名称！
        #[arg(index = 1, value_name = "label")]
        label: Option<String>
    },
    /// 修改服务器！
    Edit {
        /// 服务器名称！
        #[arg(index = 1, value_name = "label")]
        label: Option<String>
    },
}

#[derive(Subcommand)]
enum PluginCommands {
    /// 注册插件！
    Reg {},
    /// 插件列表！
    List {},
    /// 删除插件！
    Remove {
        /// 插件名称
        #[arg(index = 1, value_name = "plugin_name")]
        plugin: String
    },
}

pub async fn run_app() -> Result<()> {
    let aes_key: Vec<u8> = serde_json::from_str(AES_KEY)?;
    let aes_iv: Vec<u8> = serde_json::from_str(AES_IV)?;
    AES_KEY_ARR.set(aes_key).unwrap();
    AES_IV_ARR.set(aes_iv).unwrap();
    WORK_DIR.set(std::env::current_dir()?.to_string_lossy().to_string()).unwrap_or(());

    let cli = Cli::parse();
    match &cli.command {
        Some(Commands::Init { config }) => {
            if let Some(cnf) = config {
                let path = Path::new(cnf);
                let current_path = if path.is_file() {
                    let path = path.parent().unwrap_or_else(|| panic!("获取目录失败！"));
                    path.to_str().unwrap().to_string()
                } else {
                    cnf.to_string_lossy().to_string()
                };
                init_config(current_path)?;
            } else {
                let current_path = std::env::current_dir()?.to_str().unwrap().to_string();
                init_config(current_path)?;
            }
        }
        Some(Commands::Server { command }) => {
            let mut app = AppData::read_data()?;
            match command {
                ServerCommand::List {} => { select_server(&app, false)?; }
                ServerCommand::Add {} => {
                    add_server(&mut app)?;
                }
                ServerCommand::Show { label } => {
                    let server_name = if let Some(l) = label {
                        l.to_string()
                    } else {
                        select_server(&app, true)?
                    };
                    show_server(&server_name)?;
                }
                ServerCommand::Edit { label } => {
                    let server_name = if let Some(l) = label {
                        l.to_string()
                    } else {
                        select_server(&app, true)?
                    };
                    service::update_server(&mut app, server_name)?;
                }
                ServerCommand::Remove { label } => {
                    let server_name = if let Some(l) = label {
                        l.to_string()
                    } else {
                        select_server(&app, true)?
                    };
                    service::remove_server(&mut app, server_name)?;
                }
            }
        }
        Some(Commands::Ssh { server }) => {
            let app = AppData::read_data()?;
            let server = if let Some(label) = server {
                label.to_string()
            } else {
                select_server(&app, true)?
            };
            login_ssh(server).await?;
        }
        Some(Commands::Scp { file }) => {
            scp_file(file.clone()).await?;
        }
        Some(Commands::Edit { action, file, dir }) => {
            service::edit_file(file, vec![action.to_string()], Some(dir))?;
        }
        Some(Commands::Plugin { command }) => {
            let mut plugin_cls = service::Plugin::new()?;
            match command {
                PluginCommands::Reg {} => {
                    let name = run_cli("名称".to_string(), ParamType::String, None, None, false)?.as_string().expect("参数错误！");
                    let desc = run_cli("描述".to_string(), ParamType::String, None, None, false)?.as_string().expect("参数错误！");
                    let selects: Vec<String> = PluginScript::iter().map(|x| x.to_string()).collect();
                    let index = run_cli("脚本类型".to_string(), ParamType::Select, Some(selects.clone()), Some("0".to_string()), false)?.as_int().expect("参数错误！");
                    let exec = run_cli("可执行文件名(默认 run.py)".to_string(), ParamType::String, None, None, true)?.as_string().expect("参数错误！");
                    let requirement = run_cli("是否生成requirements.txt文件".to_string(), ParamType::Select, Some(vec!["是".to_string(), "否".to_string()]), Some("0".to_string()), false)?.as_int().expect("参数错误！");
                    plugin_cls.register(name.as_str(), PluginScript::from_str(&selects[index])?, desc.as_str(), exec.as_str(), requirement)?;
                }
                PluginCommands::List {} => {
                    plugin_cls.list_plugins()?;
                }
                PluginCommands::Remove { plugin } => {
                    plugin_cls.remove(plugin)?;
                }
            }
        }
        Some(Commands::Run { plugin, args }) => {
            let mut plugin_cls = service::Plugin::new()?;
            if let Some(params) = args {
                let params: Vec<String> = params.into_iter().map(|x| x.to_string()).collect();
                plugin_cls.run(plugin, params)?;
            } else {
                plugin_cls.run(plugin, vec![])?;
            }
        }
        None => {
            if let Some(cnf) = cli.config.as_deref() {
                let mut path = Path::new(cnf);
                if path.is_file() {
                    path = path.parent().expect("配置文件路径解析错误！");
                }
                WORK_DIR.set(path.to_string_lossy().to_string()).unwrap_or(());
            }
            let mut work = WorkUtil::new()?;
            match work.run().await {
                Ok(()) => {}
                Err(err) => error(err.to_string())
            }
        }
    }
    Ok(())
}